//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Windows.Media;
using Bots.DungeonBuddy.Behaviors;
using CommonBehaviors.Actions;
using Styx;
using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Helpers;
using Styx.Patchables;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals.World;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;


namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Mists_of_Pandaria
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Mists_of_Pandaria
#endif
{
    internal class ForgottenDepths : Dungeon
    {
        #region Overrides of Dungeon

        //verified by erenion
        private readonly WoWPoint FlamingHeadLoc = new WoWPoint(6439.94, 4534.12, -209.692);
        private readonly WoWPoint FlamingHeadMovetoLoc = new WoWPoint(6424.047, 4533.906, -209.1807);
        private readonly WoWPoint FrozenHeadLoc = new WoWPoint(6419.33, 4504.384, -209.6087);
        private readonly WoWPoint VenomousHeadLoc = new WoWPoint(6395.116, 4494.938, -209.6087);
        private readonly WoWPoint VenomousHeadMovetoLoc = new WoWPoint(6390.613, 4507.01, -209.2759);
        private readonly WoWPoint _entrance = new WoWPoint(7278.526, 5002.926, 76.17107);
        private int _diedOnTrashCount;
        private bool _runningToBoss;

        public override uint DungeonId
        {
            get { return 611; }
        }

        public override WoWPoint Entrance
        {
            get
            {
                if (_runningToBoss)
                {
                    _runningToBoss = false;
                    _diedOnTrashCount++;
                    Logger.Write("You have died {0} times on trash while trying to run to group. {1} more attempts before leaving group.", _diedOnTrashCount, 3 - _diedOnTrashCount);
                }
                return _entrance;
            }
        }

        public override void OnEnter()
        {
            _diedOnTrashCount = 0;

            //_spiderAvoidList = new List<DynamicBlackspot>
            //                {
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc1, LfgDungeon.MapId, 5, 10, "first spider"),
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc2, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc3, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc4, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc5, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc6, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidFirstSpiderLocation, () => firstSpiderAvoidLoc7, LfgDungeon.MapId, 6, 10),

            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc1, LfgDungeon.MapId, 5, 10, "second spider"),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc2, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc3, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc4 , LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc5, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc6, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc7, LfgDungeon.MapId, 6, 10),                                
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc8, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc9, LfgDungeon.MapId, 6, 10),
            //                    new DynamicBlackspot(ShouldAvoidSecondSpiderLocation, () => secondSpiderAvoidLoc10, LfgDungeon.MapId, 6, 10),
            //                };

            //DynamicBlackspotManager.AddBlackspots(_spiderAvoidList);

            if (Me.IsTank())
            {
                Alert.Show(
                    "Tanking Not Supported",
                    string.Format(
                        "Tanking is not supported in the {0} script. If you wish to stay in raid and play manually then press 'Continue'. Otherwise you will automatically leave raid.",
                        Name),
                    30,
                    true,
                    true,
                    null,
                    () => Lua.DoString("LeaveParty()"),
                    "Continue",
                    "Leave");
            }
            else
            {
                Alert.Show(
                    "Do Not AFK",
                    "It is highly recommended you do not afk while in a raid and be prepared to intervene if needed in the event something goes wrong or you're asked to perform a certain task.",
                    20,
                    true,
                    false,
                    null,
                    null,
                    "Ok");
            }
        }

        public override void OnExit()
        {
            //DynamicBlackspotManager.RemoveBlackspots(_spiderAvoidList);
            //_spiderAvoidList = null;
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            var tanks = ScriptHelpers.GroupMembers.Where(g => g.IsTank && g.Player != null).Select(g => g.Player).ToList();
            units.RemoveAll(
                o =>
                {
                    var unit = o as WoWUnit;
                    if (unit == null) return false;
                    // ignore NPCs that has no tanks near.
                    if (unit.Combat && !tanks.Any(t => t.Location.DistanceSqr(unit.Location) <= 40 * 40))
                        return true;
                    if (unit.Entry == WhirlTurleID && unit.HasAura("Shell Block"))
                        return true;
                    return false;
                });
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit == null) continue;
                if (unit.Entry == WhirlTurleID && Me.IsMelee())
                    priority.Score -= 10000;
            }
        }


        public override MoveResult MoveTo(WoWPoint location)
        {
            if (location.DistanceSqr(FlamingHeadLoc) < 10 * 10)
                return Navigator.MoveTo(FlamingHeadMovetoLoc);
            if (location.DistanceSqr(VenomousHeadLoc) < 10 * 10)
                return Navigator.MoveTo(VenomousHeadMovetoLoc);
            if (location.DistanceSqr(FrozenHeadLoc) < 10 * 10)
                return Navigator.MoveTo(VenomousHeadMovetoLoc);

            if ( !Me.IsGhost && Me.Location.DistanceSqr(location) > 100 * 100)
                _runningToBoss = true;
            else
                _runningToBoss = false;
            return MoveResult.Failed;
        }

        #endregion

        private const uint GastropodId = 68220;

        #region Root

        private List<DynamicBlackspot> _spiderAvoidList;

        private readonly WoWPoint firstSpiderAvoidLoc1 = new WoWPoint(6033.576, 4439.172, -118.6596);
        private readonly WoWPoint firstSpiderAvoidLoc2 = new WoWPoint(6023.472, 4430.746, -115.8818);
        private readonly WoWPoint firstSpiderAvoidLoc3 = new WoWPoint(6019.137, 4424.749, -115.8818);
        private readonly WoWPoint firstSpiderAvoidLoc4 = new WoWPoint(6013.34, 4415.199, -112.3895);
        private readonly WoWPoint firstSpiderAvoidLoc5 = new WoWPoint(6005.414, 4401.778, -100.4743);
        private readonly WoWPoint firstSpiderAvoidLoc6 = new WoWPoint(5999.126, 4388.885, -95.20743);
        private readonly WoWPoint firstSpiderAvoidLoc7 = new WoWPoint(5995.313, 4379.344, -95.40944);

        private readonly WoWPoint secondSpiderAvoidLoc1 = new WoWPoint(5995.313, 4379.344, -95.40944);
        private readonly WoWPoint secondSpiderAvoidLoc2 = new WoWPoint(5988.489, 4263.477, -53.58169);
        private readonly WoWPoint secondSpiderAvoidLoc3 = new WoWPoint(5989.386, 4251.143, -47.20279);
        private readonly WoWPoint secondSpiderAvoidLoc4 = new WoWPoint(5990.792, 4245.025, -42.38986);
        private readonly WoWPoint secondSpiderAvoidLoc5 = new WoWPoint(5993.023, 4238.57, -37.15385);
        private readonly WoWPoint secondSpiderAvoidLoc6 = new WoWPoint(5995.315, 4232.058, -31.91251);
        private readonly WoWPoint secondSpiderAvoidLoc7 = new WoWPoint(5997.146, 4221.018, -30.13369);
        private readonly WoWPoint secondSpiderAvoidLoc8 = new WoWPoint(6003.796, 4210.86, -21.88118);
        private readonly WoWPoint secondSpiderAvoidLoc9 = new WoWPoint(6012.618, 4194.762, -9.459324);
        private readonly WoWPoint secondSpiderAvoidLoc10 = new WoWPoint(6021.757, 4185.483, -12.2303);


        readonly WoWPoint _firstSpiderAvoidLoc = new WoWPoint(6022.224, 4431.225, -115.8814);
        readonly WoWPoint _secondSpiderAvoidLoc = new WoWPoint(5996.138, 4224.768, -30.13315);
        readonly WaitTimer _firstSpiderAvoidTimer = new WaitTimer(TimeSpan.FromSeconds(5));
        readonly WaitTimer _secondSpiderAvoidTimer = new WaitTimer(TimeSpan.FromSeconds(5));

        private const uint CorpseSpiderId = 68248;
        const uint WebId = 68249;

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(0)]
        public Composite RootEncounter()
        {
            AddAvoidObject(ctx => true, o => Me.IsMoving ? 20 : 12, u => u.Entry == GastropodId && u.ToUnit().Combat);

            return
                new PrioritySelector(
                    new Decorator(
                        ctx => _diedOnTrashCount >= 3,
                        new Action(
                            ctx =>
                                Alert.Show(
                                    "Unable to travel to group",
                                    "Automatically leaving group because bot is unable to travel to group because of getting aggro and dieing on the way. Press 'Continue' to stay",
                                    30,
                                    true,
                                    true,
                                    () => _diedOnTrashCount = 0,
                                    () => Lua.DoString("LeaveParty()"),
                                    "Continue",
                                    "Leave"))));
        }

        #endregion


        #region Tortos

        private const uint RockfallID = 68219;
        private const uint WhirlTurleID = 67966;

        [EncounterHandler(69712, "Tortos", Mode = CallBehaviorMode.Proximity)]
        public Composite TortosEncounter()
        {
            AddAvoidObject(ctx => true, 10, u => u.Entry == WhirlTurleID && ((WoWUnit)u).HasAura("Spinning Shell"));
            AddAvoidObject(ctx => true, 5, RockfallID);

            WoWUnit boss = null;
            // stay away from the spray target.
            return new PrioritySelector();
        }

        #endregion

        #region Madera

        private const uint CindersId = 70432;
        private readonly WoWPoint _cindersRunLocation = new WoWPoint(6371.334, 4548.301, -209.1768);

        [LocationHandler(6435.174, 4546.373, -209.2975, 150, "Megaera")]
        public Composite MegaeraEncounter()
        {
            bool isSwimming = false;
            WaitTimer updateRaidLocTimer = new WaitTimer(TimeSpan.FromSeconds(2));
            AddAvoidObject(ctx => true, 7, CindersId);
            WoWPoint raidLocation = WoWPoint.Zero;
            WoWUnit boss = null;
            // stay away from the spray target.
            return new PrioritySelector(
                ctx =>
                {
                    if (updateRaidLocTimer.IsFinished)
                    {
                        raidLocation = ScriptHelpers.GetGroupCenterLocation(null, 20);
                        updateRaidLocTimer.Reset();
                    }
                    return boss = Targeting.Instance.FirstUnit;
                },
                new Decorator(ctx => Me.Debuffs.ContainsKey("Cinders"), new Action(ctx => Navigator.PlayerMover.MoveTowards(_cindersRunLocation))),
                // stack up on raid location.
                new Decorator(
                    ctx =>
                    {
                        if (boss == null || !Me.Combat) return false;
                        var myLoc = Me.Location;
                        var maxDpsRange = Me.IsMelee() ? boss.MeleeRange() : 36 + boss.CombatReach;
                        if (raidLocation.Distance(boss.Location) > maxDpsRange)
                        {
                            var bossLoc = boss.Location;
                            var maxPoint = WoWMathHelper.CalculatePointFrom(raidLocation, bossLoc, maxDpsRange);
                            raidLocation = myLoc.GetNearestPointOnLine(maxPoint, bossLoc);
                        }
                        var raidDist = myLoc.Distance(raidLocation);
                        return raidLocation.Distance(boss.Location) <= maxDpsRange && raidDist > 9;
                    },
                    new Action(ctx => Navigator.MoveTo(raidLocation))),
                new Decorator(ctx => Me.IsSwimming && !isSwimming, new Action(ctx => isSwimming = true)),
                // reset current navigation path after getting ported out of water when taking the water shortcut to prevent bot from running to next node in path which is usually in the water..
                new Decorator(
                    ctx => !Me.IsSwimming && isSwimming && !Me.IsFalling && !Me.MovementInfo.IsAscending,
                    new Sequence(new Action(ctx => Navigator.NavigationProvider.Clear()), new Action(ctx => isSwimming = false))));
        }

        #endregion

        #region Ji-Kun

        private const uint FeedPoolId = 68188;
        private const int DownDraft = 134370; //pushes you off the edge
        private const uint DemonicGatewayId = 59262;
        private const uint JiKunId = 69712;
        readonly WoWPoint _jiKunPlatformLocation = new WoWPoint(6145.938, 4318.527, -31.86974);

        [EncounterHandler(69712, "Ji-Kun", Mode = CallBehaviorMode.Proximity, BossRange = 150)]
        public Composite JiKunEncounter()
        {
            WoWUnit boss = null;
            WoWUnit feedPool = null;
            // stay away from the spray target.
            AddAvoidObject(ctx => Me.HealthPercent < 60 && boss.CastingSpellId != DownDraft, 7, FeedPoolId);
            AddAvoidObject(ctx => true, 33, u => u.Entry == JiKunId && !u.ToUnit().Combat && u.ToUnit().IsAlive && u.Distance <= 50);

            var isFallingOrWhirling = new Func<bool>(() => Me.IsFalling || Me.HasAura("Safety Net Trigger "));

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(
                    ctx =>boss.HasAura(DownDraft) ||  boss.CastingSpellId == DownDraft ,
                    new Action(
                        ctx =>
                        {
                            var gateway =
                                (WoWUnit)
                                    ObjectManager.ObjectList.Where(o => o.Entry == DemonicGatewayId && o.Location.Distance2D(boss.Location) > 15)
                                        .OrderBy(u => u.Distance2DSqr)
                                        .FirstOrDefault();
                            if (gateway != null && gateway.Distance <= gateway.InteractRange)
                                gateway.Interact();
                            else
                                boss.Interact(true);
                        })),
                // disable movement while falling or flying up to platform, prevents failed navigation.
                new Decorator(ctx => isFallingOrWhirling() && ScriptHelpers.MovementEnabled, new ActionAlwaysSucceed()),
                new Decorator(ctx => boss.Distance2D > 25 && boss.Combat, new Action(ctx => Navigator.MoveTo(boss.Location))));
        }

        #endregion
    }
}